package com.milkcoffee.jvm.lesson01;

import com.milkcoffee.jvm.lesson01.exceptions.JarFullNameException;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * <p>
 * 模拟实现tomcat的类加载机制
 * 也就是在一个tomcat中存在两个不版本的类
 * </p>
 *
 * @author zxs
 */
public class TomcatClassloader extends ClassLoader {

    /**
     * 存放war包的基本路径,假设不同版本的jar包是存储在不同的路径下使用路径进行区分
     */
    private String basePath;

    private String stringTemplate = "%s/%s.class";

    private String warPrefix;

    public TomcatClassloader(String basePath, String warPrefix) {
        this.basePath = basePath;
        this.warPrefix = warPrefix;
    }

    /**
     * 将传递过来的war包转换为byte数字
     *
     * @param jarFullName war的全路径 如com.milkcoffee.demo01
     * @return
     */
    private byte[] findClazzFileAndTransForm(String jarFullName) throws IOException {
        if (jarFullName == null || jarFullName.isEmpty()) {
            throw new JarFullNameException("全路径名称不能为null或者空字符串");
        }

        String fullName = jarFullName.toLowerCase();
        String path = fullName.replaceAll("\\.", "/");
        String fullPath = String.format(stringTemplate, basePath, path);

        FileInputStream fileInputStream = new FileInputStream(fullPath);
        byte[] result = new byte[fileInputStream.available()];

        fileInputStream.read(result);
        fileInputStream.close();

        return result;
    }


    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        try {
            byte[] bytes = findClazzFileAndTransForm(name);

            return defineClass(name, bytes, 0, bytes.length);
        } catch (IOException e) {
            // 处理异常
        }

        return null;
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        synchronized (getClassLoadingLock(name)) {
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();

                if (name.startsWith(warPrefix)) {
                    c = findClass(name);
                } else {
                    c = this.getClass().getClassLoader().loadClass(name);
                }

                long t1 = System.nanoTime();
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();

            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
}
